//File   : lrecord.c 
//Author : Loon <sepnic@gmail.com> 
 
#include <stdio.h> 
#include <malloc.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <string.h> 
#include <getopt.h> 
#include <fcntl.h> 
#include <ctype.h> 
#include <errno.h> 
#include <limits.h> 
#include <time.h> 
#include <locale.h> 
#include <sys/unistd.h> 
#include <sys/stat.h> 
#include <sys/types.h> 
#include <alsa/asoundlib.h> 
#include <assert.h> 
#include "wav_parser.h" 
#include "sndwav_common.h" 
 
#define DEFAULT_CHANNELS         (1) 
#define DEFAULT_SAMPLE_RATE      (8000) 
#define DEFAULT_SAMPLE_LENGTH    (16) 
#define DEFAULT_DURATION_TIME    (10) 
 
int SNDWAV_PrepareWAVParams(WAVContainer_t *wav) 
{ 
    assert(wav); 
 
    uint16_t channels = DEFAULT_CHANNELS; 
    uint16_t sample_rate = DEFAULT_SAMPLE_RATE; 
    uint16_t sample_length = DEFAULT_SAMPLE_LENGTH; 
    uint32_t duration_time = DEFAULT_DURATION_TIME; 
 
    /* Const */ 
    wav->header.magic = WAV_RIFF; 
    wav->header.type = WAV_WAVE; 
    wav->format.magic = WAV_FMT; 
    wav->format.fmt_size = LE_INT(16); 
    wav->format.format = LE_SHORT(WAV_FMT_PCM); 
    wav->chunk.type = WAV_DATA; 
 
    /* User definition */ 
    wav->format.channels = LE_SHORT(channels); 
    wav->format.sample_rate = LE_INT(sample_rate); 
    wav->format.sample_length = LE_SHORT(sample_length); 
 
    /* See format of wav file */ 
    wav->format.blocks_align = LE_SHORT(channels * sample_length / 8); 
    wav->format.bytes_p_second = LE_INT((uint16_t)(wav->format.blocks_align) * sample_rate); 
     
    wav->chunk.length = LE_INT(duration_time * (uint32_t)(wav->format.bytes_p_second)); 
    wav->header.length = LE_INT((uint32_t)(wav->chunk.length) + sizeof(wav->chunk) + sizeof(wav->format) + sizeof(wav->header) - 8); 
 
    return 0; 
} 
 
void SNDWAV_Record(SNDPCMContainer_t *sndpcm, WAVContainer_t *wav, int fd) 
{ 
    off64_t rest; 
    size_t c, frame_size; 
     
    if (WAV_WriteHeader(fd, wav) < 0) { 
        exit(-1); 
    } 
 
    rest = wav->chunk.length; 
    while (rest > 0) { 
        c = (rest <= (off64_t)sndpcm->chunk_bytes) ? (size_t)rest : sndpcm->chunk_bytes; 
        frame_size = c * 8 / sndpcm->bits_per_frame; 
        if (SNDWAV_ReadPcm(sndpcm, frame_size) != frame_size) 
            break; 
         
        if (write(fd, sndpcm->data_buf, c) != c) { 
            fprintf(stderr, "Error SNDWAV_Record[write]\n"); 
            exit(-1); 
        } 
 
        rest -= c; 
    } 
} 
 
int main(int argc, char *argv[]) 
{ 
    char *filename; 
    char *devicename = "default"; 
    int fd; 
    WAVContainer_t wav; 
    SNDPCMContainer_t record; 
     
    if (argc != 2) { 
        fprintf(stderr, "Usage: ./lrecord <FILENAME>\n"); 
        return -1; 
    } 
     
    memset(&record, 0x0, sizeof(record)); 
 
    filename = argv[1]; 
    remove(filename); 
    if ((fd = open(filename, O_WRONLY | O_CREAT, 0644)) == -1) { 
        fprintf(stderr, "Error open: [%s]\n", filename); 
        return -1; 
    } 
 
    if (snd_output_stdio_attach(&record.log, stderr, 0) < 0) { 
        fprintf(stderr, "Error snd_output_stdio_attach\n"); 
        goto Err; 
    } 
 
    if (snd_pcm_open(&record.handle, devicename, SND_PCM_STREAM_CAPTURE, 0) < 0) { 
        fprintf(stderr, "Error snd_pcm_open [ %s]\n", devicename); 
        goto Err; 
    } 
 
    if (SNDWAV_PrepareWAVParams(&wav) < 0) { 
        fprintf(stderr, "Error SNDWAV_PrepareWAVParams\n"); 
        goto Err; 
    } 
 
    if (SNDWAV_SetParams(&record, &wav) < 0) { 
        fprintf(stderr, "Error set_snd_pcm_params\n"); 
        goto Err; 
    } 
    snd_pcm_dump(record.handle, record.log); 
 
    SNDWAV_Record(&record, &wav, fd); 
 
    snd_pcm_drain(record.handle); 
 
    close(fd); 
    free(record.data_buf); 
    snd_output_close(record.log); 
    snd_pcm_close(record.handle); 
    return 0; 
 
Err: 
    close(fd); 
    remove(filename); 
    if (record.data_buf) free(record.data_buf); 
    if (record.log) snd_output_close(record.log); 
    if (record.handle) snd_pcm_close(record.handle); 
    return -1; 
} 